package com.niit.onlinemusicserver.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.niit.onlinemusicserver.common.R;
import com.niit.onlinemusicserver.common.RandomCodeGenerate;
import com.niit.onlinemusicserver.config.AlipayCommonConfig;
import com.niit.onlinemusicserver.constant.Constants;
import com.niit.onlinemusicserver.mapper.PayItemMapper;
import com.niit.onlinemusicserver.mapper.PayMapper;
import com.niit.onlinemusicserver.mapper.SongMapper;
import com.niit.onlinemusicserver.model.domain.Pay;
import com.niit.onlinemusicserver.model.domain.PayItem;
import com.niit.onlinemusicserver.model.domain.Song;
import com.niit.onlinemusicserver.service.PayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Service
public class PayServiceImpl implements PayService {

  @Autowired
  private SongMapper songMapper;

  @Autowired
  private PayMapper payMapper;

  @Autowired
  private PayItemMapper payItemMapper;

  @Autowired
  private AlipayCommonConfig alipayCommonConfig;

  @Autowired
  private StringRedisTemplate redisTemplate;

  //@Autowired
  //private CartListMapper cartListMapper;

  /**
   * 支付宝沙箱通用支付功能
   * @param songIds 歌曲ID集合
   * @param userId 用户ID
   * @param payType 支付类型 1表示直接支付 2表示购物车结算
   * @return
   * @throws Exception
   */
  @Override
  public R commonAlipay(List<Integer> songIds, Integer userId, Integer payType) {

    if(songIds == null || songIds.size() < 1){
      R.error("接口传入参数错误");
    }

    String alipayResult = null;

    try {
      List<Song> songs = songMapper.selectBatchIds(songIds);
      if(songs == null || songs.size() < 1){
        return R.error("查询歌曲数据错误");
      }

      //组装支付清单和清单子项并求清单的总金额
      //产生清单编号
      String payNo = RandomCodeGenerate.createOrderNo();
      List<PayItem> payItems = new ArrayList<>();
      BigDecimal totalAmount = BigDecimal.valueOf(0.0);
      for (Song s: songs) {
        PayItem item = new PayItem();
        item.setPrice(s.getPrice());
        item.setSongId(s.getId());
        item.setTradeNo(payNo);
        item.setUserId(userId);
        payItems.add(item);
        BigDecimal songPrice = BigDecimal.valueOf(item.getPrice());
        totalAmount = totalAmount.add(songPrice);
      }

      //组装订单
      Pay pay = new Pay();
      pay.setPayStatus(Constants.PAY_STATUS_1);
      pay.setPayType(payType);
      pay.setSubject("歌曲下载");
      pay.setAmount(totalAmount.doubleValue());
      pay.setTradeNo(payNo);
      pay.setUserId(userId);
      pay.setPayItems(payItems);

      //付款清单和清单子项暂存，待支付成功后核销
      redisTemplate.opsForValue().set(Constants.ALIPAY_REDIS_KEY + pay.getTradeNo().trim(),
              JSONObject.toJSONString(pay));

      //获得初始化的AlipayClient
      AlipayClient alipayClient = new DefaultAlipayClient(alipayCommonConfig.getGateway_url(),
              alipayCommonConfig.getApp_id(),
              alipayCommonConfig.getApp_private_key(),
              alipayCommonConfig.getFormat(),alipayCommonConfig.getCharset(),
              alipayCommonConfig.getAlipay_public_key(),
              alipayCommonConfig.getSign_type());

      //设置请求参数
      AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
      //同步回调
      alipayRequest.setReturnUrl(alipayCommonConfig.getReturn_url());
      //支付宝支付成功服务器异步回调核销
      alipayRequest.setNotifyUrl(alipayCommonConfig.getNotify_url());

      Map<String,Object> bizContent = new HashMap<>();
      //订单号
      bizContent.put("out_trade_no",pay.getTradeNo());
      //销售产品码，与支付宝签约的产品码名称。目前仅支付FAST_INSTANT_TRADE_PAY
      bizContent.put("product_code","FAST_INSTANT_TRADE_PAY");
      //订单金额
      bizContent.put("total_amount",pay.getAmount());
      //中文乱码处理
      bizContent.put("subject",new String(pay.getSubject().getBytes("UTF-8"), "UTF-8"));
      alipayRequest.setBizContent(JSONObject.toJSONString(bizContent));
      alipayResult = alipayClient.pageExecute(alipayRequest).getBody();
    }catch (AlipayApiException e){
      e.printStackTrace();
      return R.error("请求支付宝异常 | "+e.getMessage());
    }catch (Exception e){
      e.printStackTrace();
      return R.error("接口异常 | "+e.getMessage());
    }
    return R.success("",alipayResult);
  }

  /**
   * 支付宝沙箱支付服务器端异步回调功能，应用上线后可以和同步并用
   * @param request
   * @return
   * @throws Exception
   */
  @Override
  @Transactional( rollbackFor=Exception.class)
  public R commonPayNotify(HttpServletRequest request) throws Exception{

    System.out.println("支付宝通用支付回调");

    if(request.getParameter("trade_status").equals("TRADE_SUCCESS")){

      Map<String, String> params = new HashMap<>();
      Map<String, String[]> parameterMap = request.getParameterMap();
      for (String name: parameterMap.keySet() ) {
        params.put(name, request.getParameter(name));
      }

      //订单交易号=商户订单号
      String outTradeNo = params.get("out_trade_no");
      //买家付款时间
      String gmtPayment = params.get("gmt_payment");
      //支付宝的交易凭证
      String alipayNo = params.get("trade_no");
      //签名
      String sign = params.get("sign");
      String contentV1 = AlipaySignature.getSignCheckContentV1(params);
      boolean checkSignature = AlipaySignature.rsa256CheckContent(contentV1,
              sign, alipayCommonConfig.getAlipay_public_key(), alipayCommonConfig.getCharset());

      //支付宝验签
      R r = null;
      if(checkSignature){
        //订单子项核销，更新订单状态并保存订单子项
        String payStr = redisTemplate.opsForValue().get(Constants.ALIPAY_REDIS_KEY + outTradeNo.trim());
        r = afterPayBusiness(payStr, alipayNo);
        //核销后清空缓存
        redisTemplate.delete(Constants.ALIPAY_REDIS_KEY + outTradeNo.trim());
      }else {
        return R.error("支付验签失败，需手动核销清单项");
      }
      return r;
    }
    return R.error("支付回调失败，需手动核销清单项");
  }

  /**
   * 当应用上线后，方法只负责跳转，不做复杂的核销业务，
   * 核销业务应在异步回调中执行
   * @param tradeNo
   * @param sign
   * @param response
   * @throws Exception
   */
  @Override
  @Transactional( rollbackFor=Exception.class)
  public void commonPayReturn(String tradeNo, String sign, HttpServletResponse response) throws Exception {
    //订单子项核销，更新订单状态并保存订单子项
    String payStr = redisTemplate.opsForValue().get(Constants.ALIPAY_REDIS_KEY + tradeNo.trim());
    afterPayBusiness(payStr, tradeNo);
    //核销后清空缓存
    redisTemplate.delete(Constants.ALIPAY_REDIS_KEY + tradeNo.trim());
    response.setHeader("refresh", "1;url="+alipayCommonConfig.getRefresh_url());
  }

  private R afterPayBusiness(String payStr, String alipayNo){

    if(payStr == null){
      return R.error("数据从缓存中取出错误");
    }

    Pay pay = JSONObject.parseObject(payStr, Pay.class);

    if(pay == null){
      return R.error("缓存数据转换类型错误");
    }

    //支付清单保存
    pay.setAlipayNo(alipayNo);
    payMapper.insert(pay);
    //支付清单子项保存，后期可以优化批量插入
    List<PayItem> payItems = pay.getPayItems();
    payItems.forEach(payItem -> payItemMapper.insert(payItem));

    //如果是购物车结算，即payType=2
    if(pay.getPayType() == 2){
      List<Integer> clistIds = new ArrayList<>();
      payItems.forEach(p -> clistIds.add(p.getSongId()));
      //cartListMapper.deleteBatchIds(clistIds);
    }
    return R.success("支付核销成功");
  }

  /**
   * 返回某用户的所有支付的歌曲清单
   * @param userId
   * @return
   */
  @Override
  public R allPayList(Integer userId) {
    QueryWrapper<PayItem> qw = new QueryWrapper<>();
    qw.eq("user_id",userId);
    return R.success("",payItemMapper.selectList(qw));
  }

}
